package mua.core.interpreter;

import mua.core.operation.MuaOperation;
import mua.core.value.Number;
import mua.core.value.*;

import java.util.ArrayList;
import java.util.Scanner;
import java.util.Stack;

import static mua.core.interpreter.Util.*;

/**
 * 解析器，解析一行代码并判断是否合法
 * @author 彭子帆
 * @date 2020年10月7日
 */
public class Parse {
    protected static void parseAndRun(){
        Scanner scanner = new Scanner(System.in);
        StringBuilder one = new StringBuilder();
        String line;
        //开始读入
        while(scanner.hasNext()){
            //read one line
            line=scanner.nextLine();
            //为空则返回
            if(line.isEmpty()){
                continue;
            }
            one.append(line).append(" ");
            //遇到关键字或者函数名后，后续所有的操作都在该语句中，直到操作栈为空
            int index = one.indexOf(" ");
            //该行输入舍弃的条件为：第一个不为操作符
            if(index==-1) {if(!MuaOperation.contains(one.toString(),null)) continue;}
            else if(!MuaOperation.contains(one.substring(0,index),null)) continue;
            //处理该行
            if(parseLine(one.toString(), scanner,null)!=null){
                //运行成功，清空该行
                one = new StringBuilder();
            }
        }
    }

    /** 由于是前缀表达式，从后向前扫描
     * @param line 输入的一行
     * @param scanner 必须将scanner作为参数传入，不能另外创建新的scanner对象，否则无法通过read
     * @param functionBody null代表全局变量
     */
    public static MuaValue parseLine(String line, Scanner scanner, FunctionBody functionBody){
        //分别存储操作和操作数的栈
        Stack<String> operationStack = new Stack<>();
        Stack<MuaValue> valueStack = new Stack<>();
        //方法一：非递归，p3阶段有bug，需要调整
//        ArrayList<String> parts = mySplit(line);
//        String s;
//        for(int i =parts.size()-1;i>0;i--){
//            s = parts.get(i);
//            if(s!=null&&!s.isEmpty()) parseOne(operationStack,valueStack,s,scanner,functionBody);
//        }
//        return parseOne(operationStack,valueStack,parts.get(0),scanner,functionBody);

        //方法二：递归处理
        //判断最后一部分为操作或数值
        String[] part = split(myTrim(line));
        while(part[1]!=null&&!part[1].isEmpty()){
            parseOne(operationStack,valueStack,part[1], scanner, functionBody);
            part = split(part[0]);
        }
        return parseOne(operationStack,valueStack,part[0], scanner, functionBody);
    }

    /**
     * 每次输入一个value或operation都运行一次
     * @param s 最后一部分字符串
     * @return 如果运行成功则返回true
     */
    private static MuaValue parseOne(Stack<String> operationStack, Stack<MuaValue> valueStack,
                                     String s, Scanner scanner, FunctionBody functionBody) {
        if(MuaOperation.contains(s,functionBody)){
            operationStack.push(s);
        }
        else {
            //此时需要放入valueStack
            char initial = s.charAt(0);
            String subString = s.substring(1);
            //字
            if(initial=='"'){
                //判断是否为布尔
                if("true".equals(subString)){
                    valueStack.push(new Bool("true"));
                }else if("false".equals(subString)){
                    valueStack.push(new Bool("false"));
                }else if(isNumber(subString)){
                    valueStack.push(new Number(subString));
                }
                else{
                    valueStack.push(new Word(subString));
                }
            }
            //数字类型
            else if(isNumber(s)){
                valueStack.push(new Number(s));
            }
            //布尔型
            else if("true".equals(s)){
                valueStack.push(new Bool("true"));
            }else if("false".equals(s)){
                valueStack.push(new Bool("false"));
            }
            //表达式，表达式内允许出现所有前缀表达式（sub,add...）
            else if(initial=='('){
                String expression = subString.substring(0,subString.lastIndexOf(')'));
                //判断是否有如"add, make, eq"等前缀表达式，有的话需要先处理
                if(containsPrefix(expression)){
//                    valueStack.push(parseLine(expression,scanner,functionBody));
                    //处理前缀
                    expression = handlePrefix(expression,scanner,functionBody);
                }
                //没有则直接进行中缀表达式处理
                valueStack.push(ArithmeticExpression.parseExpression(expression, functionBody));
            }
            //取值操作，:a相当于thing a
            else if(initial==':'){
                operationStack.push("thing");
                valueStack.push(new Word(subString));
                //尝试进行解析，此处传参是引用传参而非值传递
                return MuaOperation.run(operationStack,valueStack,scanner,functionBody);
            }
            //判断是否为'[]'包含
            else if(s.charAt(0)=='['){
                //如果是函数
                if(isFunction(s)){
                    valueStack.push(new FunctionBody(s, functionBody));
                }
                //如果是表达式
                else if(isExpression(s,functionBody)){
                    valueStack.push(new Expression(s));
                }
                //暂时认为只有函数体分多行进行输入
                //如果括号未闭合，则进行多行输入
                else if(!isBracketClosed(s)){
                    valueStack.push(readFunctionBody(s, scanner, functionBody));
                }
                //如果是列表
                else{
                    valueStack.push(new List(s));
                }
            }
            //名字
            else{
                valueStack.push(new Word(s));
            }
        }
        //尝试进行解析，此处传参是引用传参而非值传递
        return MuaOperation.run(operationStack,valueStack,scanner,functionBody);
    }

    /**
     * 将一个含前缀表达式的式子中的前缀表达式转为具体的值，返回仅中缀表达式
     * @param expression 含前缀表达式的中缀表达式
     * @param scanner scanner
     * @param functionBody functionBody
     * @return 返回仅中缀表达式字符串
     */
    private static String handlePrefix(String expression,Scanner scanner,FunctionBody functionBody) {
        StringBuilder one, ret = new StringBuilder();
        ArrayList<String> parts = mySplit(myTrim(expression));
        String s;
        //记录所有操作，直到所有操作结束，一条命令结束
        Stack<String> operations = new Stack<>();
        //记录value的个数
        int values = 0;
        for(int i=0;i<parts.size();i++) {
            s = parts.get(i);
            if (MuaOperation.contains(s, functionBody)) {
                //一句话
                one = new StringBuilder();
                while (i < parts.size()) {
                    s = parts.get(i);
                    one.append(s).append(" ");
                    //判断为操作符或操作数
                    if (MuaOperation.contains(s, functionBody)) operations.push(s);
                    else values++;
                    //尝试是否为一句话
                    if (isOneSentence(operations, values, functionBody)) {
                        //如果是一句话则，将加入到ret中
                        ret.append(Parse.parseLine(one.toString(), scanner, functionBody)).append(" ");
                        break;
                    }
                    i++;
                }
            } else {
                ret.append(s).append(" ");
            }
        }
        return myTrim(ret.toString());
    }

    /**
     * 判断该行的中括号是否闭合，暂时认为不闭合即为函数体
     * @param s 该行的字符串
     * @return 返回是否闭合
     */
    private static boolean isBracketClosed(String s) {
        int left = 0, right = 0;
        for(int i=0;i<s.length();i++){
            if(s.charAt(i)=='[')left++;
            else if(s.charAt(i)==']')right++;
        }
        return left==right;
    }

    /**
     * 我们认为函数体可能分为多行
     * 用来读取函数体，接下来几行都通过该函数输入，直到中括号闭合
     * @param s 该行字符串
     * @param scanner scanner
     * @return 返回该行结束后有没有成功生成出FunctionBody
     */
    private static MuaValue readFunctionBody(String s, Scanner scanner, FunctionBody fb) {
        //Store the brackets "[" "]", judge whether they are closed.
        Stack<Boolean> brackets = new Stack<>();
        StringBuilder functionBody = new StringBuilder();
        char temp;
        //刚进来的字符串的扫描，一个字符一个字符地扫描
        for(int i=0;i<s.length();i++){
            temp = s.charAt(i);
            if(temp==']'){
                brackets.pop();
            }else if(temp=='['){
                brackets.push(true);
            }
            functionBody.append(temp);
        }
        //后续字符串的扫描，直到中括号闭合
        while (scanner.hasNext()){
            //读取一行前先在其前加空格
            functionBody.append(" ");
            //读取一行并按照上面相同方式扫描
            s = scanner.nextLine();
            for(int i=0;i<s.length();i++){
                temp = s.charAt(i);
                if(temp==']'){
                    brackets.pop();
                    if(brackets.isEmpty()){
                        functionBody.append("]");
                        return new FunctionBody(functionBody.toString(), fb);
                    }
                }else if(temp=='['){
                    brackets.push(true);
                }
                functionBody.append(temp);
            }
        }
        return null;
    }

    /**
     * 判断一个括号内如"(add (5*3) 3)"是否包含前缀表达式"add", "eq"等
     * @param expression 括号内的字符串，如上式的"add (5*3) 3"
     * @return 如果包含前缀operator，则返回true
     */
    private static boolean containsPrefix(String expression) {
        //先将其中的"(",")","+","-","*","/","%"都替换为" "
        String replaced = expression.replace('(',' ').replace(')',' ')
                .replace('+',' ').replace('-',' ')
                .replace('*',' ').replace('/',' ')
                .replace('%',' ');
        String[] parts = replaced.split(" ");
        for(String s: parts){
            //如果每部分为数字或者为字符但开头为":"，则正确，否则为operator
            if(!s.isEmpty()&&!isNumber(s)&&s.charAt(0)!=':'){
                return true;
            }
        }
        return false;
    }

    /**
     * 输入个字符串，以最后一个空格分割两部分，如果最后一部分被小（中）括号包含则忽略空格。
     * 若无空格或整行以“[]”包裹的，仅返回第一部分，第二部分设为null。
     * @param str String
     * @return 返回第一个空格(或tab)前后两部分
     */
    private static String[] split(String str){
        //如果最后一项是表达式
        if (str.charAt(str.length()-1)==')'){
            //用来存放小括号，来判断最后一部分的括号包裹的内容
            Stack<Boolean> brackets = new Stack<>();
            //先将最后一个小括号push进去，同时要将最左index直接减去2
            brackets.push(true);
            int leftIndex = str.length()-2;
            while(leftIndex>=0&&!brackets.empty()){
                if(str.charAt(leftIndex)=='('){
                    brackets.pop();
                }else if(str.charAt(leftIndex)==')'){
                    brackets.push(true);
                }
                leftIndex--;
            }
            if(leftIndex<=0)return new String[]{str,null};
            else return new String[]{str.substring(0,leftIndex),str.substring(leftIndex+1)};
        }
        //如果最后一项是列表
        if (str.charAt(str.length()-1)==']'){
            //用来存放中括号，来判断最后一部分的括号包裹的内容
            Stack<Boolean> brackets = new Stack<>();
            //先将最后一个中括号push进去，同时要将最左index直接减去2
            brackets.push(true);
            int leftIndex = str.length()-2;
            while(leftIndex>=0&&!brackets.empty()){
                if(str.charAt(leftIndex)=='['){
                    brackets.pop();
                }else if(str.charAt(leftIndex)==']'){
                    brackets.push(true);
                }
                leftIndex--;
            }
            if(leftIndex<=0)return new String[]{str,null};
            else return new String[]{str.substring(0,leftIndex),str.substring(leftIndex+1)};
        }
        //如果后一项不是表达式
        int index = str.lastIndexOf(' ');
        if(index==-1){
            return new String[]{str,null};
        } else{
            return new String[]{str.substring(0,index),str.substring(index+1)};
        }
    }
}
